﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mogre;
using MogreNewt;

namespace WastedMechanics
{
    static class Engine
    {
        public enum Modes
        {
            GameMode,
            EditorMode
        }

        public static Root Root;
        public static RenderWindow RenderWindow;
        public static SceneManager SceneManager;
        public static Camera Camera;
        public static Viewport Viewport;
        public static MOIS.Keyboard Keyboard;
        public static MOIS.Mouse Mouse;
        public static MOIS.InputManager InputManager;
        public static World NewtonWorld;
        public static Debugger NewtonDebugger;

        public static Level CurrentLevel;
        public static GameCamera GameCamera;
        public static EditorCamera EditorCamera;
        public static ObjectManager ObjectManager;
        static int BodyId;

        public const float FixedFPS = 30.0f;
        public const float FixedTimeStep = 1.0f / FixedFPS;
        public static float TimeAccumulator;
        public static long LastTime;
        public static float TimeStep;        

        public static MaterialManager MaterialManager;

        public static HUDController HUDController;
        public static HumanController HumanController;
        public static TypedInput TypedInput;
        public static MouseInput MouseInput;

        public static GuiManager GuiManager;
        public static Modes Mode;

        public static bool ExitProgram;
        public static Action CustomCallback;

        public static SoundManager SoundManager;
        public static Vector3 CameraVelocity;

        public static Dictionary<string, DescribedProfile> DescribedProfiles;
        public static Dictionary<string, ContainerProfile> ContainerProfiles;
        public static Dictionary<string, CharacterProfile> CharacterProfiles;
        public static Dictionary<string, Item> ItemTemplates;

        public static Dictionary<string, bool> WorldFlags;

        public static void FlagOff(string name) {
            WorldFlags[name] = false;
            Console.WriteLine("Flag \"" + name + "\" = false");
        }
        public static void FlagOn(string name) {
            WorldFlags[name] = true;
            Console.WriteLine("Flag \"" + name + "\" = true");
        }
        public static bool GetFlag(string name)
        {
            bool returnValue;
            if (!WorldFlags.ContainsKey(name))
                returnValue = false;
            else
                returnValue = WorldFlags[name];
            Console.WriteLine("Flagread \"" + name + "\" = false");
            return returnValue;
        }
        
     


        public static void Initialise()
        {
            SetupOgre();
            
            GameCamera = new GameCamera();
            EditorCamera = new EditorCamera();
            ObjectManager = new ObjectManager();
            SoundManager = new SoundManager();
            
            MaterialManager = new MaterialManager();
            MaterialManager.Initialise();

            GuiManager = new GuiManager();
            HumanController = new HumanController();
            HUDController = new HUDController();

            TypedInput = new TypedInput();
            MouseInput = new MouseInput();

            DescribedProfiles = new Dictionary<string, DescribedProfile>();
            ContainerProfiles = new Dictionary<string, ContainerProfile>();
            CharacterProfiles = new Dictionary<string, CharacterProfile>();
            ItemTemplates = new Dictionary<string, Item>();

            WorldFlags = new Dictionary<string, bool>();
        }

        private static void SetupOgre()
        {
            Root = new Root();
            ConfigFile cf = new ConfigFile();
            cf.Load("Resources.cfg", "\t:=", true);

            ConfigFile.SectionIterator seci = cf.GetSectionIterator();

            while (seci.MoveNext())
            {
                ConfigFile.SettingsMultiMap settings = seci.Current;
                foreach (KeyValuePair<string, string> pair in settings)
                    ResourceGroupManager.Singleton.AddResourceLocation(pair.Value, pair.Key, seci.CurrentKey);
            }

            if (!Root.RestoreConfig())
                if (!Root.ShowConfigDialog())
                    return;

            RenderWindow = Root.Initialise(true);
            ResourceGroupManager.Singleton.InitialiseAllResourceGroups();

            SceneManager = Root.CreateSceneManager(SceneType.ST_GENERIC);
            Camera = SceneManager.CreateCamera("MainCamera");
            Viewport = RenderWindow.AddViewport(Camera);
            Camera.AspectRatio = (float)(RenderWindow.Width) / (float)(RenderWindow.Height);
            Camera.NearClipDistance = 0.05f;
            Camera.FarClipDistance = 1000.0f;
            Camera.FOVy = new Degree(60.0f);                       

            MOIS.ParamList pl = new MOIS.ParamList();
            IntPtr windowHnd;
            RenderWindow.GetCustomAttribute("WINDOW", out windowHnd);
            pl.Insert("WINDOW", windowHnd.ToString());
            //pl.Insert("w32_mouse","DISCL_FOREGROUND");
            //pl.Insert("w32_mouse", "DISCL_NONEXCLUSIVE");

            InputManager = MOIS.InputManager.CreateInputSystem(pl);

            Keyboard = (MOIS.Keyboard)InputManager.CreateInputObject(MOIS.Type.OISKeyboard, false);
            Mouse = (MOIS.Mouse)InputManager.CreateInputObject(MOIS.Type.OISMouse, true);            

            NewtonWorld = new World();
            NewtonDebugger = new Debugger(NewtonWorld);
            NewtonDebugger.Init(SceneManager);                        
        }

        public static string GetResourcePath(string resourceName)
        {
            FileInfoListPtr res = ResourceGroupManager.Singleton.FindResourceFileInfo("General", resourceName);
            FileInfo_NativePtr fi = res[0];

            string fullpath =  System.IO.Path.Combine(fi.archive.Name, fi.filename);

            res.Dispose();
            return fullpath;
        }

        public static bool IsKeyTyped(MOIS.KeyCode code)
        {
            return TypedInput.IsKeyTyped[(int)code];
        }

        public static void Update()
        {
            long currentTime = Root.Timer.Milliseconds;
            TimeStep = (currentTime - LastTime) / 1000.0f;
            LastTime = currentTime;
            TimeAccumulator += TimeStep;

            TimeAccumulator = System.Math.Min(FixedTimeStep * 4, TimeAccumulator);            
                                    
            Root.RenderOneFrame();

            bool firstFrame = true; 
            while (TimeAccumulator >= FixedTimeStep)
            {              
                TimeAccumulator -= FixedTimeStep;
                if (firstFrame)
                {
                    Keyboard.Capture();
                    TypedInput.Update();
                    firstFrame = false;
                }
                Mouse.Capture(); 
                MouseInput.Update();

                if (CustomCallback != null) CustomCallback();

                UpdateModeSwitchKeys();
                switch (Mode)
                {
                    case Modes.GameMode:
                        NewtonWorld.Update(FixedTimeStep);
                        HumanController.Update();
                        HUDController.Update();
                        ObjectManager.Update();                        
                        break;
                    case Modes.EditorMode:
                        EditorCamera.Update();
                        break;
                }
                GuiManager.Update();
                MouseInput.ResetClicks();               
                TypedInput.ClearTypedKeys();
                SoundManager.Update();
            }
            UpdateModeSwitchKeys();            

            WindowEventUtilities.MessagePump();            
        }

        public static void UpdateModeSwitchKeys()
        {
            if (IsKeyTyped(MOIS.KeyCode.KC_F4))
                Mode = Modes.GameMode;
            if (IsKeyTyped(MOIS.KeyCode.KC_F5))
                Mode = Modes.EditorMode;
        }

        public static int GetUniqueBodyId()
        {
            return BodyId++;
        }

        public static float CurrentFPS
        {
            get
            {
                return RenderWindow.LastFPS;
            }
        }
    }
}
